Sensor Fusion for Kinetis MCUs (ISSDK/KSDK version)
orientation.c
Go to the documentation of this file.
1 // Copyright (c) 2014, 2015, 2016, NXP Semiconductors N.V.,
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are met:
6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above copyright
9 // notice, this list of conditions and the following disclaimer in the
10 // documentation and/or other materials provided with the distribution.
11 // * Neither the name of NXP Semiconductors N.V. nor the
12 // names of its contributors may be used to endorse or promote products
13 // derived from this software without specific prior written permission.
14 //
15 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
16 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18 // DISCLAIMED. IN NO EVENT SHALL NXP SEMICONDUCTORS N.V. BE LIABLE FOR ANY
19 // DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
20 // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 // LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
22 // ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
24 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 // This file contains functions designed to operate on, or compute, orientations.
26 // These may be in rotation matrix form, quaternion form, or Euler angles.
27 // It also includes functions designed to operate with specify reference frames
28 // (Android, Windows 8, NED).
29 //
30 
31 /*! \file orientation.c
32  \brief Functions to convert between various orientation representations
33 
34  Functions to convert between various orientation representations. Also
35  includes functions for manipulating quaternions.
36 */
37 
38 #include "stdio.h"
39 #include "math.h"
40 #include "stdlib.h"
41 #include "time.h"
42 #include "string.h"
43 
44 #include "sensor_fusion.h"
45 #include "orientation.h"
46 #include "fusion.h"
47 #include "matrix.h"
48 #include "approximations.h"
49 
50 // compile time constants that are private to this file
51 #define SMALLQ0 1E-4F // limit of quaternion scalar component requiring special algorithm
52 #define CORRUPTQUAT 0.001F // threshold for deciding rotation quaternion is corrupt
53 #define SMALLMODULUS 0.01F // limit where rounding errors may appear
54 
55 #if F_USING_ACCEL // Need tilt conversion routines
56 // Aerospace NED accelerometer 3DOF tilt function computing rotation matrix fR
57 #if (THISCOORDSYSTEM == NED) || (THISCOORDSYSTEM == ANDROID)
58 void f3DOFTiltNED(float fR[][3], float fGc[])
59 {
60  // the NED self-consistency twist occurs at 90 deg pitch
61 
62  // local variables
63  int16 i; // counter
64  float fmodGxyz; // modulus of the x, y, z accelerometer readings
65  float fmodGyz; // modulus of the y, z accelerometer readings
66  float frecipmodGxyz; // reciprocal of modulus
67  float ftmp; // scratch variable
68 
69  // compute the accelerometer squared magnitudes
70  fmodGyz = fGc[CHY] * fGc[CHY] + fGc[CHZ] * fGc[CHZ];
71  fmodGxyz = fmodGyz + fGc[CHX] * fGc[CHX];
72 
73  // check for freefall special case where no solution is possible
74  if (fmodGxyz == 0.0F)
75  {
76  f3x3matrixAeqI(fR);
77  return;
78  }
79 
80  // check for vertical up or down gimbal lock case
81  if (fmodGyz == 0.0F)
82  {
83  f3x3matrixAeqScalar(fR, 0.0F);
84  fR[CHY][CHY] = 1.0F;
85  if (fGc[CHX] >= 0.0F)
86  {
87  fR[CHX][CHZ] = 1.0F;
88  fR[CHZ][CHX] = -1.0F;
89  }
90  else
91  {
92  fR[CHX][CHZ] = -1.0F;
93  fR[CHZ][CHX] = 1.0F;
94  }
95  return;
96  }
97 
98  // compute moduli for the general case
99  fmodGyz = sqrtf(fmodGyz);
100  fmodGxyz = sqrtf(fmodGxyz);
101  frecipmodGxyz = 1.0F / fmodGxyz;
102  ftmp = fmodGxyz / fmodGyz;
103 
104  // normalize the accelerometer reading into the z column
105  for (i = CHX; i <= CHZ; i++)
106  {
107  fR[i][CHZ] = fGc[i] * frecipmodGxyz;
108  }
109 
110  // construct x column of orientation matrix
111  fR[CHX][CHX] = fmodGyz * frecipmodGxyz;
112  fR[CHY][CHX] = -fR[CHX][CHZ] * fR[CHY][CHZ] * ftmp;
113  fR[CHZ][CHX] = -fR[CHX][CHZ] * fR[CHZ][CHZ] * ftmp;
114 
115  // construct y column of orientation matrix
116  fR[CHX][CHY] = 0.0F;
117  fR[CHY][CHY] = fR[CHZ][CHZ] * ftmp;
118  fR[CHZ][CHY] = -fR[CHY][CHZ] * ftmp;
119 
120  return;
121 }
122 #endif // #if THISCOORDSYSTEM == NED
123 
124 // Android accelerometer 3DOF tilt function computing rotation matrix fR
125 #if THISCOORDSYSTEM == ANDROID
126 void f3DOFTiltAndroid(float fR[][3], float fGc[])
127 {
128  // the Android tilt matrix is mathematically identical to the NED tilt matrix
129  // the Android self-consistency twist occurs at 90 deg roll
130  f3DOFTiltNED(fR, fGc);
131  return;
132 }
133 #endif // #if THISCOORDSYSTEM == ANDROID
134 
135 // Windows 8 accelerometer 3DOF tilt function computing rotation matrix fR
136 #if (THISCOORDSYSTEM == WIN8)
137 void f3DOFTiltWin8(float fR[][3], float fGc[])
138 {
139  // the Win8 self-consistency twist occurs at 90 deg roll
140 
141  // local variables
142  float fmodGxyz; // modulus of the x, y, z accelerometer readings
143  float fmodGxz; // modulus of the x, z accelerometer readings
144  float frecipmodGxyz; // reciprocal of modulus
145  float ftmp; // scratch variable
146  int8 i; // counter
147 
148  // compute the accelerometer squared magnitudes
149  fmodGxz = fGc[CHX] * fGc[CHX] + fGc[CHZ] * fGc[CHZ];
150  fmodGxyz = fmodGxz + fGc[CHY] * fGc[CHY];
151 
152  // check for freefall special case where no solution is possible
153  if (fmodGxyz == 0.0F)
154  {
155  f3x3matrixAeqI(fR);
156  return;
157  }
158 
159  // check for vertical up or down gimbal lock case
160  if (fmodGxz == 0.0F)
161  {
162  f3x3matrixAeqScalar(fR, 0.0F);
163  fR[CHX][CHX] = 1.0F;
164  if (fGc[CHY] >= 0.0F)
165  {
166  fR[CHY][CHZ] = -1.0F;
167  fR[CHZ][CHY] = 1.0F;
168  }
169  else
170  {
171  fR[CHY][CHZ] = 1.0F;
172  fR[CHZ][CHY] = -1.0F;
173  }
174  return;
175  }
176 
177  // compute moduli for the general case
178  fmodGxz = sqrtf(fmodGxz);
179  fmodGxyz = sqrtf(fmodGxyz);
180  frecipmodGxyz = 1.0F / fmodGxyz;
181  ftmp = fmodGxyz / fmodGxz;
182  if (fGc[CHZ] < 0.0F)
183  {
184  ftmp = -ftmp;
185  }
186 
187  // normalize the negated accelerometer reading into the z column
188  for (i = CHX; i <= CHZ; i++)
189  {
190  fR[i][CHZ] = -fGc[i] * frecipmodGxyz;
191  }
192 
193  // construct x column of orientation matrix
194  fR[CHX][CHX] = -fR[CHZ][CHZ] * ftmp;
195  fR[CHY][CHX] = 0.0F;
196  fR[CHZ][CHX] = fR[CHX][CHZ] * ftmp;;
197 
198  // // construct y column of orientation matrix
199  fR[CHX][CHY] = fR[CHX][CHZ] * fR[CHY][CHZ] * ftmp;
200  fR[CHY][CHY] = -fmodGxz * frecipmodGxyz;
201  if (fGc[CHZ] < 0.0F)
202  {
203  fR[CHY][CHY] = -fR[CHY][CHY];
204  }
205  fR[CHZ][CHY] = fR[CHY][CHZ] * fR[CHZ][CHZ] * ftmp;
206 
207  return;
208 }
209 #endif // #if (THISCOORDSYSTEM == WIN8)
210 #endif // #if F_USING_ACCEL // Need tilt conversion routines
211 
212 #if F_USING_MAG // Need eCompass conversion routines
213 // Aerospace NED magnetometer 3DOF flat eCompass function computing rotation matrix fR
214 #if THISCOORDSYSTEM == NED
215 void f3DOFMagnetometerMatrixNED(float fR[][3], float fBc[])
216 {
217  // local variables
218  float fmodBxy; // modulus of the x, y magnetometer readings
219 
220  // compute the magnitude of the horizontal (x and y) magnetometer reading
221  fmodBxy = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY]);
222 
223  // check for zero field special case where no solution is possible
224  if (fmodBxy == 0.0F)
225  {
226  f3x3matrixAeqI(fR);
227  return;
228  }
229 
230  // define the fixed entries in the z row and column
231  fR[CHZ][CHX] = fR[CHZ][CHY] = fR[CHX][CHZ] = fR[CHY][CHZ] = 0.0F;
232  fR[CHZ][CHZ] = 1.0F;
233 
234  // define the remaining entries
235  fR[CHX][CHX] = fR[CHY][CHY] = fBc[CHX] / fmodBxy;
236  fR[CHY][CHX] = fBc[CHY] / fmodBxy;
237  fR[CHX][CHY] = -fR[CHY][CHX];
238 
239  return;
240 }
241 #endif // #if THISCOORDSYSTEM == NED
242 
243 // Android magnetometer 3DOF flat eCompass function computing rotation matrix fR
244 #if (THISCOORDSYSTEM == ANDROID) || (THISCOORDSYSTEM == WIN8)
245 void f3DOFMagnetometerMatrixAndroid(float fR[][3], float fBc[])
246 {
247  // local variables
248  float fmodBxy; // modulus of the x, y magnetometer readings
249 
250  // compute the magnitude of the horizontal (x and y) magnetometer reading
251  fmodBxy = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY]);
252 
253  // check for zero field special case where no solution is possible
254  if (fmodBxy == 0.0F)
255  {
256  f3x3matrixAeqI(fR);
257  return;
258  }
259 
260  // define the fixed entries in the z row and column
261  fR[CHZ][CHX] = fR[CHZ][CHY] = fR[CHX][CHZ] = fR[CHY][CHZ] = 0.0F;
262  fR[CHZ][CHZ] = 1.0F;
263 
264  // define the remaining entries
265  fR[CHX][CHX] = fR[CHY][CHY] = fBc[CHY] / fmodBxy;
266  fR[CHX][CHY] = fBc[CHX] / fmodBxy;
267  fR[CHY][CHX] = -fR[CHX][CHY];
268 
269  return;
270 }
271 #endif // #if THISCOORDSYSTEM == ANDROID
272 
273 // Windows 8 magnetometer 3DOF flat eCompass function computing rotation matrix fR
274 #if (THISCOORDSYSTEM == WIN8)
275 void f3DOFMagnetometerMatrixWin8(float fR[][3], float fBc[])
276 {
277  // call the Android function since it is identical to the Windows 8 matrix
279 
280  return;
281 }
282 #endif // #if (THISCOORDSYSTEM == WIN8)
283 
284 // NED: basic 6DOF e-Compass function computing rotation matrix fR and magnetic inclination angle fDelta
285 #if THISCOORDSYSTEM == NED
286 void feCompassNED(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
287 {
288  // local variables
289  float fmod[3]; // column moduli
290  float fGcdotBc; // dot product of vectors G.Bc
291  float ftmp; // scratch variable
292  int8 i, j; // loop counters
293 
294  // set the inclination angle to zero in case it is not computed later
295  *pfDelta = *pfsinDelta = 0.0F;
296  *pfcosDelta = 1.0F;
297 
298  // place the un-normalized gravity and geomagnetic vectors into the rotation matrix z and x axes
299  for (i = CHX; i <= CHZ; i++)
300  {
301  fR[i][CHZ] = fGc[i];
302  fR[i][CHX] = fBc[i];
303  }
304 
305  // set y vector to vector product of z and x vectors
306  fR[CHX][CHY] = fR[CHY][CHZ] * fR[CHZ][CHX] - fR[CHZ][CHZ] * fR[CHY][CHX];
307  fR[CHY][CHY] = fR[CHZ][CHZ] * fR[CHX][CHX] - fR[CHX][CHZ] * fR[CHZ][CHX];
308  fR[CHZ][CHY] = fR[CHX][CHZ] * fR[CHY][CHX] - fR[CHY][CHZ] * fR[CHX][CHX];
309 
310  // set x vector to vector product of y and z vectors
311  fR[CHX][CHX] = fR[CHY][CHY] * fR[CHZ][CHZ] - fR[CHZ][CHY] * fR[CHY][CHZ];
312  fR[CHY][CHX] = fR[CHZ][CHY] * fR[CHX][CHZ] - fR[CHX][CHY] * fR[CHZ][CHZ];
313  fR[CHZ][CHX] = fR[CHX][CHY] * fR[CHY][CHZ] - fR[CHY][CHY] * fR[CHX][CHZ];
314 
315  // calculate the rotation matrix column moduli
316  fmod[CHX] = sqrtf(fR[CHX][CHX] * fR[CHX][CHX] + fR[CHY][CHX] * fR[CHY][CHX] + fR[CHZ][CHX] * fR[CHZ][CHX]);
317  fmod[CHY] = sqrtf(fR[CHX][CHY] * fR[CHX][CHY] + fR[CHY][CHY] * fR[CHY][CHY] + fR[CHZ][CHY] * fR[CHZ][CHY]);
318  fmod[CHZ] = sqrtf(fR[CHX][CHZ] * fR[CHX][CHZ] + fR[CHY][CHZ] * fR[CHY][CHZ] + fR[CHZ][CHZ] * fR[CHZ][CHZ]);
319 
320  // normalize the rotation matrix columns
321  if (!((fmod[CHX] == 0.0F) || (fmod[CHY] == 0.0F) || (fmod[CHZ] == 0.0F)))
322  {
323  // loop over columns j
324  for (j = CHX; j <= CHZ; j++)
325  {
326  ftmp = 1.0F / fmod[j];
327  // loop over rows i
328  for (i = CHX; i <= CHZ; i++)
329  {
330  // normalize by the column modulus
331  fR[i][j] *= ftmp;
332  }
333  }
334  }
335  else
336  {
337  // no solution is possible so set rotation to identity matrix
338  f3x3matrixAeqI(fR);
339  return;
340  }
341 
342  // compute the geomagnetic inclination angle (deg)
343  *pfmodGc = fmod[CHZ];
344  *pfmodBc = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY] + fBc[CHZ] * fBc[CHZ]);
345  fGcdotBc = fGc[CHX] * fBc[CHX] + fGc[CHY] * fBc[CHY] + fGc[CHZ] * fBc[CHZ];
346  if (!((*pfmodGc == 0.0F) || (*pfmodBc == 0.0F)))
347  {
348  *pfsinDelta = fGcdotBc / (*pfmodGc * *pfmodBc);
349  *pfcosDelta = sqrtf(1.0F - *pfsinDelta * *pfsinDelta);
350  *pfDelta = fasin_deg(*pfsinDelta);
351  }
352 
353  return;
354 }
355 #endif // #if THISCOORDSYSTEM == NED
356 
357 // Android: basic 6DOF e-Compass function computing rotation matrix fR and magnetic inclination angle fDelta
358 #if THISCOORDSYSTEM == ANDROID
359 void feCompassAndroid(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[],
360  float *pfmodBc, float *pfmodGc)
361 {
362  // local variables
363  float fmod[3]; // column moduli
364  float fGcdotBc; // dot product of vectors G.Bc
365  float ftmp; // scratch variable
366  int8 i, j; // loop counters
367 
368  // set the inclination angle to zero in case it is not computed later
369  *pfDelta = *pfsinDelta = 0.0F;
370  *pfcosDelta = 1.0F;
371 
372  // place the un-normalized gravity and geomagnetic vectors into the rotation matrix z and y axes
373  for (i = CHX; i <= CHZ; i++)
374  {
375  fR[i][CHZ] = fGc[i];
376  fR[i][CHY] = fBc[i];
377  }
378 
379  // set x vector to vector product of y and z vectors
380  fR[CHX][CHX] = fR[CHY][CHY] * fR[CHZ][CHZ] - fR[CHZ][CHY] * fR[CHY][CHZ];
381  fR[CHY][CHX] = fR[CHZ][CHY] * fR[CHX][CHZ] - fR[CHX][CHY] * fR[CHZ][CHZ];
382  fR[CHZ][CHX] = fR[CHX][CHY] * fR[CHY][CHZ] - fR[CHY][CHY] * fR[CHX][CHZ];
383 
384  // set y vector to vector product of z and x vectors
385  fR[CHX][CHY] = fR[CHY][CHZ] * fR[CHZ][CHX] - fR[CHZ][CHZ] * fR[CHY][CHX];
386  fR[CHY][CHY] = fR[CHZ][CHZ] * fR[CHX][CHX] - fR[CHX][CHZ] * fR[CHZ][CHX];
387  fR[CHZ][CHY] = fR[CHX][CHZ] * fR[CHY][CHX] - fR[CHY][CHZ] * fR[CHX][CHX];
388 
389  // calculate the rotation matrix column moduli
390  fmod[CHX] = sqrtf(fR[CHX][CHX] * fR[CHX][CHX] + fR[CHY][CHX] * fR[CHY][CHX] + fR[CHZ][CHX] * fR[CHZ][CHX]);
391  fmod[CHY] = sqrtf(fR[CHX][CHY] * fR[CHX][CHY] + fR[CHY][CHY] * fR[CHY][CHY] + fR[CHZ][CHY] * fR[CHZ][CHY]);
392  fmod[CHZ] = sqrtf(fR[CHX][CHZ] * fR[CHX][CHZ] + fR[CHY][CHZ] * fR[CHY][CHZ] + fR[CHZ][CHZ] * fR[CHZ][CHZ]);
393 
394  // normalize the rotation matrix columns
395  if (!((fmod[CHX] == 0.0F) || (fmod[CHY] == 0.0F) || (fmod[CHZ] == 0.0F)))
396  {
397  // loop over columns j
398  for (j = CHX; j <= CHZ; j++)
399  {
400  ftmp = 1.0F / fmod[j];
401  // loop over rows i
402  for (i = CHX; i <= CHZ; i++)
403  {
404  // normalize by the column modulus
405  fR[i][j] *= ftmp;
406  }
407  }
408  }
409  else
410  {
411  // no solution is possible so set rotation to identity matrix
412  f3x3matrixAeqI(fR);
413  return;
414  }
415 
416  // compute the geomagnetic inclination angle (deg)
417  *pfmodGc = fmod[CHZ];
418  *pfmodBc = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY] + fBc[CHZ] * fBc[CHZ]);
419  fGcdotBc = fGc[CHX] * fBc[CHX] + fGc[CHY] * fBc[CHY] + fGc[CHZ] * fBc[CHZ];
420  if (!((*pfmodGc == 0.0F) || (*pfmodBc == 0.0F)))
421  {
422  *pfsinDelta = -fGcdotBc / (*pfmodGc * *pfmodBc);
423  *pfcosDelta = sqrtf(1.0F - *pfsinDelta * *pfsinDelta);
424  *pfDelta = fasin_deg(*pfsinDelta);
425  }
426 
427  return;
428 }
429 #endif // #if THISCOORDSYSTEM == ANDROID
430 
431 // Win8: basic 6DOF e-Compass function computing rotation matrix fR and magnetic inclination angle fDelta
432 #if (THISCOORDSYSTEM == WIN8)
433 void feCompassWin8(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[],
434  float *pfmodBc, float *pfmodGc)
435 {
436  // local variables
437  float fmod[3]; // column moduli
438  float fGcdotBc; // dot product of vectors G.Bc
439  float ftmp; // scratch variable
440  int8 i, j; // loop counters
441 
442  // set the inclination angle to zero in case it is not computed later
443  *pfDelta = *pfsinDelta = 0.0F;
444  *pfcosDelta = 1.0F;
445 
446  // place the negated un-normalized gravity and un-normalized geomagnetic vectors into the rotation matrix z and y axes
447  for (i = CHX; i <= CHZ; i++)
448  {
449  fR[i][CHZ] = -fGc[i];
450  fR[i][CHY] = fBc[i];
451  }
452 
453  // set x vector to vector product of y and z vectors
454  fR[CHX][CHX] = fR[CHY][CHY] * fR[CHZ][CHZ] - fR[CHZ][CHY] * fR[CHY][CHZ];
455  fR[CHY][CHX] = fR[CHZ][CHY] * fR[CHX][CHZ] - fR[CHX][CHY] * fR[CHZ][CHZ];
456  fR[CHZ][CHX] = fR[CHX][CHY] * fR[CHY][CHZ] - fR[CHY][CHY] * fR[CHX][CHZ];
457 
458  // set y vector to vector product of z and x vectors
459  fR[CHX][CHY] = fR[CHY][CHZ] * fR[CHZ][CHX] - fR[CHZ][CHZ] * fR[CHY][CHX];
460  fR[CHY][CHY] = fR[CHZ][CHZ] * fR[CHX][CHX] - fR[CHX][CHZ] * fR[CHZ][CHX];
461  fR[CHZ][CHY] = fR[CHX][CHZ] * fR[CHY][CHX] - fR[CHY][CHZ] * fR[CHX][CHX];
462 
463  // calculate the rotation matrix column moduli
464  fmod[CHX] = sqrtf(fR[CHX][CHX] * fR[CHX][CHX] + fR[CHY][CHX] * fR[CHY][CHX] + fR[CHZ][CHX] * fR[CHZ][CHX]);
465  fmod[CHY] = sqrtf(fR[CHX][CHY] * fR[CHX][CHY] + fR[CHY][CHY] * fR[CHY][CHY] + fR[CHZ][CHY] * fR[CHZ][CHY]);
466  fmod[CHZ] = sqrtf(fR[CHX][CHZ] * fR[CHX][CHZ] + fR[CHY][CHZ] * fR[CHY][CHZ] + fR[CHZ][CHZ] * fR[CHZ][CHZ]);
467 
468  // normalize the rotation matrix columns
469  if (!((fmod[CHX] == 0.0F) || (fmod[CHY] == 0.0F) || (fmod[CHZ] == 0.0F)))
470  {
471  // loop over columns j
472  for (j = CHX; j <= CHZ; j++)
473  {
474  ftmp = 1.0F / fmod[j];
475  // loop over rows i
476  for (i = CHX; i <= CHZ; i++)
477  {
478  // normalize by the column modulus
479  fR[i][j] *= ftmp;
480  }
481  }
482  }
483  else
484  {
485  // no solution is possible so set rotation to identity matrix
486  f3x3matrixAeqI(fR);
487  return;
488  }
489 
490  // compute the geomagnetic inclination angle (deg)
491  *pfmodGc = fmod[CHZ];
492  *pfmodBc = sqrtf(fBc[CHX] * fBc[CHX] + fBc[CHY] * fBc[CHY] + fBc[CHZ] * fBc[CHZ]);
493  fGcdotBc = fGc[CHX] * fBc[CHX] + fGc[CHY] * fBc[CHY] + fGc[CHZ] * fBc[CHZ];
494  if (!((*pfmodGc == 0.0F) || (*pfmodBc == 0.0F)))
495  {
496  *pfsinDelta = fGcdotBc / (*pfmodGc * *pfmodBc);
497  *pfcosDelta = sqrtf(1.0F - *pfsinDelta * *pfsinDelta);
498  *pfDelta = fasin_deg(*pfsinDelta);
499  }
500 
501  return;
502 }
503 #endif // #if (THISCOORDSYSTEM == WIN8)
504 #endif // #if F_USING_MAG // Need eCompass conversion routines
505 
506 // extract the NED angles in degrees from the NED rotation matrix
507 #if THISCOORDSYSTEM == NED
508 void fNEDAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg,
509  float *pfRhoDeg, float *pfChiDeg)
510 {
511  // calculate the pitch angle -90.0 <= Theta <= 90.0 deg
512  *pfTheDeg = fasin_deg(-R[CHX][CHZ]);
513 
514  // calculate the roll angle range -180.0 <= Phi < 180.0 deg
515  *pfPhiDeg = fatan2_deg(R[CHY][CHZ], R[CHZ][CHZ]);
516 
517  // map +180 roll onto the functionally equivalent -180 deg roll
518  if (*pfPhiDeg == 180.0F)
519  {
520  *pfPhiDeg = -180.0F;
521  }
522 
523  // calculate the yaw (compass) angle 0.0 <= Psi < 360.0 deg
524  if (*pfTheDeg == 90.0F)
525  {
526  // vertical upwards gimbal lock case
527  *pfPsiDeg = fatan2_deg(R[CHZ][CHY], R[CHY][CHY]) + *pfPhiDeg;
528  }
529  else if (*pfTheDeg == -90.0F)
530  {
531  // vertical downwards gimbal lock case
532  *pfPsiDeg = fatan2_deg(-R[CHZ][CHY], R[CHY][CHY]) - *pfPhiDeg;
533  }
534  else
535  {
536  // general case
537  *pfPsiDeg = fatan2_deg(R[CHX][CHY], R[CHX][CHX]);
538  }
539 
540  // map yaw angle Psi onto range 0.0 <= Psi < 360.0 deg
541  if (*pfPsiDeg < 0.0F)
542  {
543  *pfPsiDeg += 360.0F;
544  }
545 
546  // check for rounding errors mapping small negative angle to 360 deg
547  if (*pfPsiDeg >= 360.0F)
548  {
549  *pfPsiDeg = 0.0F;
550  }
551 
552  // for NED, the compass heading Rho equals the yaw angle Psi
553  *pfRhoDeg = *pfPsiDeg;
554 
555  // calculate the tilt angle from vertical Chi (0 <= Chi <= 180 deg)
556  *pfChiDeg = facos_deg(R[CHZ][CHZ]);
557 
558  return;
559 }
560 #endif // #if THISCOORDSYSTEM == NED
561 
562 // extract the Android angles in degrees from the Android rotation matrix
563 #if THISCOORDSYSTEM == ANDROID
564 void fAndroidAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg,
565  float *pfRhoDeg, float *pfChiDeg)
566 {
567  // calculate the roll angle -90.0 <= Phi <= 90.0 deg
568  *pfPhiDeg = fasin_deg(R[CHX][CHZ]);
569 
570  // calculate the pitch angle -180.0 <= The < 180.0 deg
571  *pfTheDeg = fatan2_deg(-R[CHY][CHZ], R[CHZ][CHZ]);
572 
573  // map +180 pitch onto the functionally equivalent -180 deg pitch
574  if (*pfTheDeg == 180.0F)
575  {
576  *pfTheDeg = -180.0F;
577  }
578 
579  // calculate the yaw (compass) angle 0.0 <= Psi < 360.0 deg
580  if (*pfPhiDeg == 90.0F)
581  {
582  // vertical downwards gimbal lock case
583  *pfPsiDeg = fatan2_deg(R[CHY][CHX], R[CHY][CHY]) - *pfTheDeg;
584  }
585  else if (*pfPhiDeg == -90.0F)
586  {
587  // vertical upwards gimbal lock case
588  *pfPsiDeg = fatan2_deg(R[CHY][CHX], R[CHY][CHY]) + *pfTheDeg;
589  }
590  else
591  {
592  // general case
593  *pfPsiDeg = fatan2_deg(-R[CHX][CHY], R[CHX][CHX]);
594  }
595 
596  // map yaw angle Psi onto range 0.0 <= Psi < 360.0 deg
597  if (*pfPsiDeg < 0.0F)
598  {
599  *pfPsiDeg += 360.0F;
600  }
601 
602  // check for rounding errors mapping small negative angle to 360 deg
603  if (*pfPsiDeg >= 360.0F)
604  {
605  *pfPsiDeg = 0.0F;
606  }
607 
608  // the compass heading angle Rho equals the yaw angle Psi
609  // this definition is compliant with Motorola Xoom tablet behavior
610  *pfRhoDeg = *pfPsiDeg;
611 
612  // calculate the tilt angle from vertical Chi (0 <= Chi <= 180 deg)
613  *pfChiDeg = facos_deg(R[CHZ][CHZ]);
614 
615  return;
616 }
617 #endif // #if THISCOORDSYSTEM == ANDROID
618 
619 // extract the Windows 8 angles in degrees from the Windows 8 rotation matrix
620 #if (THISCOORDSYSTEM == WIN8)
621 void fWin8AnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg,
622  float *pfRhoDeg, float *pfChiDeg)
623 {
624  // calculate the roll angle -90.0 <= Phi <= 90.0 deg
625  if (R[CHZ][CHZ] == 0.0F)
626  {
627  if (R[CHX][CHZ] >= 0.0F)
628  {
629  // tan(phi) is -infinity
630  *pfPhiDeg = -90.0F;
631  }
632  else
633  {
634  // tan(phi) is +infinity
635  *pfPhiDeg = 90.0F;
636  }
637  }
638  else
639  {
640  // general case
641  *pfPhiDeg = fatan_deg(-R[CHX][CHZ] / R[CHZ][CHZ]);
642  }
643 
644  // first calculate the pitch angle The in the range -90.0 <= The <= 90.0 deg
645  *pfTheDeg = fasin_deg(R[CHY][CHZ]);
646 
647  // use R[CHZ][CHZ]=cos(Phi)*cos(The) to correct the quadrant of The remembering
648  // cos(Phi) is non-negative so that cos(The) has the same sign as R[CHZ][CHZ].
649  if (R[CHZ][CHZ] < 0.0F)
650  {
651  // wrap The around +90 deg and -90 deg giving result 90 to 270 deg
652  *pfTheDeg = 180.0F - *pfTheDeg;
653  }
654 
655  // map the pitch angle The to the range -180.0 <= The < 180.0 deg
656  if (*pfTheDeg >= 180.0F)
657  {
658  *pfTheDeg -= 360.0F;
659  }
660 
661  // calculate the yaw angle Psi
662  if (*pfTheDeg == 90.0F)
663  {
664  // vertical upwards gimbal lock case: -270 <= Psi < 90 deg
665  *pfPsiDeg = fatan2_deg(R[CHX][CHY], R[CHX][CHX]) - *pfPhiDeg;
666  }
667  else if (*pfTheDeg == -90.0F)
668  {
669  // vertical downwards gimbal lock case: -270 <= Psi < 90 deg
670  *pfPsiDeg = fatan2_deg(R[CHX][CHY], R[CHX][CHX]) + *pfPhiDeg;
671  }
672  else
673  {
674  // general case: -180 <= Psi < 180 deg
675  *pfPsiDeg = fatan2_deg(-R[CHY][CHX], R[CHY][CHY]);
676 
677  // correct the quadrant for Psi using the value of The (deg) to give -180 <= Psi < 380 deg
678  if (fabsf(*pfTheDeg) >= 90.0F)
679  {
680  *pfPsiDeg += 180.0F;
681  }
682  }
683 
684  // map yaw angle Psi onto range 0.0 <= Psi < 360.0 deg
685  if (*pfPsiDeg < 0.0F)
686  {
687  *pfPsiDeg += 360.0F;
688  }
689 
690  // check for any rounding error mapping small negative angle to 360 deg
691  if (*pfPsiDeg >= 360.0F)
692  {
693  *pfPsiDeg = 0.0F;
694  }
695 
696  // compute the compass angle Rho = 360 - Psi
697  *pfRhoDeg = 360.0F - *pfPsiDeg;
698 
699  // check for rounding errors mapping small negative angle to 360 deg and zero degree case
700  if (*pfRhoDeg >= 360.0F)
701  {
702  *pfRhoDeg = 0.0F;
703  }
704 
705  // calculate the tilt angle from vertical Chi (0 <= Chi <= 180 deg)
706  *pfChiDeg = facos_deg(R[CHZ][CHZ]);
707 
708  return;
709 }
710 #endif // #if (THISCOORDSYSTEM == WIN8)
711 
712 // computes normalized rotation quaternion from a rotation vector (deg)
713 void fQuaternionFromRotationVectorDeg(Quaternion *pq, const float rvecdeg[], float fscaling)
714 {
715  float fetadeg; // rotation angle (deg)
716  float fetarad; // rotation angle (rad)
717  float fetarad2; // eta (rad)^2
718  float fetarad4; // eta (rad)^4
719  float sinhalfeta; // sin(eta/2)
720  float fvecsq; // q1^2+q2^2+q3^2
721  float ftmp; // scratch variable
722 
723  // compute the scaled rotation angle eta (deg) which can be both positve or negative
724  fetadeg = fscaling * sqrtf(rvecdeg[CHX] * rvecdeg[CHX] + rvecdeg[CHY] * rvecdeg[CHY] + rvecdeg[CHZ] * rvecdeg[CHZ]);
725  fetarad = fetadeg * FPIOVER180;
726  fetarad2 = fetarad * fetarad;
727 
728  // calculate the sine and cosine using small angle approximations or exact
729  // angles under sqrt(0.02)=0.141 rad is 8.1 deg and 1620 deg/s (=936deg/s in 3 axes) at 200Hz and 405 deg/s at 50Hz
730  if (fetarad2 <= 0.02F)
731  {
732  // use MacLaurin series up to and including third order
733  sinhalfeta = fetarad * (0.5F - ONEOVER48 * fetarad2);
734  }
735  else if (fetarad2 <= 0.06F)
736  {
737  // use MacLaurin series up to and including fifth order
738  // angles under sqrt(0.06)=0.245 rad is 14.0 deg and 2807 deg/s (=1623deg/s in 3 axes) at 200Hz and 703 deg/s at 50Hz
739  fetarad4 = fetarad2 * fetarad2;
740  sinhalfeta = fetarad * (0.5F - ONEOVER48 * fetarad2 + ONEOVER3840 * fetarad4);
741  }
742  else
743  {
744  // use exact calculation
745  sinhalfeta = (float)sinf(0.5F * fetarad);
746  }
747 
748  // compute the vector quaternion components q1, q2, q3
749  if (fetadeg != 0.0F)
750  {
751  // general case with non-zero rotation angle
752  ftmp = fscaling * sinhalfeta / fetadeg;
753  pq->q1 = rvecdeg[CHX] * ftmp; // q1 = nx * sin(eta/2)
754  pq->q2 = rvecdeg[CHY] * ftmp; // q2 = ny * sin(eta/2)
755  pq->q3 = rvecdeg[CHZ] * ftmp; // q3 = nz * sin(eta/2)
756  }
757  else
758  {
759  // zero rotation angle giving zero vector component
760  pq->q1 = pq->q2 = pq->q3 = 0.0F;
761  }
762 
763  // compute the scalar quaternion component q0 by explicit normalization
764  // taking care to avoid rounding errors giving negative operand to sqrt
765  fvecsq = pq->q1 * pq->q1 + pq->q2 * pq->q2 + pq->q3 * pq->q3;
766  if (fvecsq <= 1.0F)
767  {
768  // normal case
769  pq->q0 = sqrtf(1.0F - fvecsq);
770  }
771  else
772  {
773  // rounding errors are present
774  pq->q0 = 0.0F;
775  }
776 
777  return;
778 }
779 
780 // compute the orientation quaternion from a 3x3 rotation matrix
782 {
783  float fq0sq; // q0^2
784  float recip4q0; // 1/4q0
785 
786  // get q0^2 and q0
787  fq0sq = 0.25F * (1.0F + R[CHX][CHX] + R[CHY][CHY] + R[CHZ][CHZ]);
788  pq->q0 = sqrtf(fabsf(fq0sq));
789 
790  // normal case when q0 is not small meaning rotation angle not near 180 deg
791  if (pq->q0 > SMALLQ0)
792  {
793  // calculate q1 to q3
794  recip4q0 = 0.25F / pq->q0;
795  pq->q1 = recip4q0 * (R[CHY][CHZ] - R[CHZ][CHY]);
796  pq->q2 = recip4q0 * (R[CHZ][CHX] - R[CHX][CHZ]);
797  pq->q3 = recip4q0 * (R[CHX][CHY] - R[CHY][CHX]);
798  } // end of general case
799  else
800  {
801  // special case of near 180 deg corresponds to nearly symmetric matrix
802  // which is not numerically well conditioned for division by small q0
803  // instead get absolute values of q1 to q3 from leading diagonal
804  pq->q1 = sqrtf(fabsf(0.5F + 0.5F * R[CHX][CHX] - fq0sq));
805  pq->q2 = sqrtf(fabsf(0.5F + 0.5F * R[CHY][CHY] - fq0sq));
806  pq->q3 = sqrtf(fabsf(0.5F + 0.5F * R[CHZ][CHZ] - fq0sq));
807 
808  // correct the signs of q1 to q3 by examining the signs of differenced off-diagonal terms
809  if ((R[CHY][CHZ] - R[CHZ][CHY]) < 0.0F) pq->q1 = -pq->q1;
810  if ((R[CHZ][CHX] - R[CHX][CHZ]) < 0.0F) pq->q2 = -pq->q2;
811  if ((R[CHX][CHY] - R[CHY][CHX]) < 0.0F) pq->q3 = -pq->q3;
812  } // end of special case
813 
814  // ensure that the resulting quaternion is normalized even if the input rotation matrix was not
815  fqAeqNormqA(pq);
816 
817 
818  return;
819 }
820 
821 // compute the rotation matrix from an orientation quaternion
822 void fRotationMatrixFromQuaternion(float R[][3], const Quaternion *pq)
823 {
824  float f2q;
825  float f2q0q0, f2q0q1, f2q0q2, f2q0q3;
826  float f2q1q1, f2q1q2, f2q1q3;
827  float f2q2q2, f2q2q3;
828  float f2q3q3;
829 
830  // set f2q to 2*q0 and calculate products
831  f2q = 2.0F * pq->q0;
832  f2q0q0 = f2q * pq->q0;
833  f2q0q1 = f2q * pq->q1;
834  f2q0q2 = f2q * pq->q2;
835  f2q0q3 = f2q * pq->q3;
836  // set f2q to 2*q1 and calculate products
837  f2q = 2.0F * pq->q1;
838  f2q1q1 = f2q * pq->q1;
839  f2q1q2 = f2q * pq->q2;
840  f2q1q3 = f2q * pq->q3;
841  // set f2q to 2*q2 and calculate products
842  f2q = 2.0F * pq->q2;
843  f2q2q2 = f2q * pq->q2;
844  f2q2q3 = f2q * pq->q3;
845  f2q3q3 = 2.0F * pq->q3 * pq->q3;
846 
847  // calculate the rotation matrix assuming the quaternion is normalized
848  R[CHX][CHX] = f2q0q0 + f2q1q1 - 1.0F;
849  R[CHX][CHY] = f2q1q2 + f2q0q3;
850  R[CHX][CHZ] = f2q1q3 - f2q0q2;
851  R[CHY][CHX] = f2q1q2 - f2q0q3;
852  R[CHY][CHY] = f2q0q0 + f2q2q2 - 1.0F;
853  R[CHY][CHZ] = f2q2q3 + f2q0q1;
854  R[CHZ][CHX] = f2q1q3 + f2q0q2;
855  R[CHZ][CHY] = f2q2q3 - f2q0q1;
856  R[CHZ][CHZ] = f2q0q0 + f2q3q3 - 1.0F;
857 
858  return;
859 }
860 
861 // computes rotation vector (deg) from rotation quaternion
863 {
864  float fetarad; // rotation angle (rad)
865  float fetadeg; // rotation angle (deg)
866  float sinhalfeta; // sin(eta/2)
867  float ftmp; // scratch variable
868 
869  // calculate the rotation angle in the range 0 <= eta < 360 deg
870  if ((pq->q0 >= 1.0F) || (pq->q0 <= -1.0F))
871  {
872  // rotation angle is 0 deg or 2*180 deg = 360 deg = 0 deg
873  fetarad = 0.0F;
874  fetadeg = 0.0F;
875  }
876  else
877  {
878  // general case returning 0 < eta < 360 deg
879  fetarad = 2.0F * acosf(pq->q0);
880  fetadeg = fetarad * F180OVERPI;
881  }
882 
883  // map the rotation angle onto the range -180 deg <= eta < 180 deg
884  if (fetadeg >= 180.0F)
885  {
886  fetadeg -= 360.0F;
887  fetarad = fetadeg * FPIOVER180;
888  }
889 
890  // calculate sin(eta/2) which will be in the range -1 to +1
891  sinhalfeta = (float)sinf(0.5F * fetarad);
892 
893  // calculate the rotation vector (deg)
894  if (sinhalfeta == 0.0F)
895  {
896  // the rotation angle eta is zero and the axis is irrelevant
897  rvecdeg[CHX] = rvecdeg[CHY] = rvecdeg[CHZ] = 0.0F;
898  }
899  else
900  {
901  // general case with non-zero rotation angle
902  ftmp = fetadeg / sinhalfeta;
903  rvecdeg[CHX] = pq->q1 * ftmp;
904  rvecdeg[CHY] = pq->q2 * ftmp;
905  rvecdeg[CHZ] = pq->q3 * ftmp;
906  }
907 
908  return;
909 }
910 
911 // function low pass filters an orientation quaternion and computes virtual gyro rotation rate
912 void fLPFOrientationQuaternion(Quaternion *pq, Quaternion *pLPq, float flpf, float fdeltat, float fOmega[])
913 {
914  // local variables
915  Quaternion fdeltaq; // delta rotation quaternion
916  float rvecdeg[3]; // rotation vector (deg)
917  float ftmp; // scratch variable
918 
919  // set fdeltaq to the delta rotation quaternion conjg(fLPq[n-1) . fqn
920  fdeltaq = qconjgAxB(pLPq, pq);
921  if (fdeltaq.q0 < 0.0F)
922  {
923  fdeltaq.q0 = -fdeltaq.q0;
924  fdeltaq.q1 = -fdeltaq.q1;
925  fdeltaq.q2 = -fdeltaq.q2;
926  fdeltaq.q3 = -fdeltaq.q3;
927  }
928 
929  // set ftmp to a scaled value which equals flpf in the limit of small rotations (q0=1)
930  // but which rises to 1 (all pass) as the delta rotation angle increases (q0 tends to zero)
931  ftmp = flpf + (1.0F - flpf) * sqrtf(fabs(1.0F - fdeltaq.q0 * fdeltaq.q0));
932 
933  // scale the vector component of the delta rotation quaternion by the corrected lpf value
934  // and re-compute the scalar component q0 to ensure normalization
935  fdeltaq.q1 *= ftmp;
936  fdeltaq.q2 *= ftmp;
937  fdeltaq.q3 *= ftmp;
938  fdeltaq.q0 = sqrtf(fabsf(1.0F - fdeltaq.q1 * fdeltaq.q1 - fdeltaq.q2 * fdeltaq.q2 - fdeltaq.q3 * fdeltaq.q3));
939 
940  // calculate the delta rotation vector from fdeltaq and the virtual gyro angular velocity (deg/s)
941  fRotationVectorDegFromQuaternion(&fdeltaq, rvecdeg);
942  ftmp = 1.0F / fdeltat;
943  fOmega[CHX] = rvecdeg[CHX] * ftmp;
944  fOmega[CHY] = rvecdeg[CHY] * ftmp;
945  fOmega[CHZ] = rvecdeg[CHZ] * ftmp;
946 
947  // set LPq[n] = LPq[n-1] . deltaq[n]
948  qAeqAxB(pLPq, &fdeltaq);
949 
950  // renormalize the low pass filtered quaternion to prevent error accumulation.
951  // the renormalization function also ensures that q0 is non-negative
952  fqAeqNormqA(pLPq);
953 
954  return;
955 }
956 
957 // function compute the quaternion product qA * qB
958 void qAeqBxC(Quaternion *pqA, const Quaternion *pqB, const Quaternion *pqC)
959 {
960  pqA->q0 = pqB->q0 * pqC->q0 - pqB->q1 * pqC->q1 - pqB->q2 * pqC->q2 - pqB->q3 * pqC->q3;
961  pqA->q1 = pqB->q0 * pqC->q1 + pqB->q1 * pqC->q0 + pqB->q2 * pqC->q3 - pqB->q3 * pqC->q2;
962  pqA->q2 = pqB->q0 * pqC->q2 - pqB->q1 * pqC->q3 + pqB->q2 * pqC->q0 + pqB->q3 * pqC->q1;
963  pqA->q3 = pqB->q0 * pqC->q3 + pqB->q1 * pqC->q2 - pqB->q2 * pqC->q1 + pqB->q3 * pqC->q0;
964 
965  return;
966 }
967 
968 // function compute the quaternion product qA = qA * qB
969 void qAeqAxB(Quaternion *pqA, const Quaternion *pqB)
970 {
971  Quaternion qProd;
972 
973  // perform the quaternion product
974  qProd.q0 = pqA->q0 * pqB->q0 - pqA->q1 * pqB->q1 - pqA->q2 * pqB->q2 - pqA->q3 * pqB->q3;
975  qProd.q1 = pqA->q0 * pqB->q1 + pqA->q1 * pqB->q0 + pqA->q2 * pqB->q3 - pqA->q3 * pqB->q2;
976  qProd.q2 = pqA->q0 * pqB->q2 - pqA->q1 * pqB->q3 + pqA->q2 * pqB->q0 + pqA->q3 * pqB->q1;
977  qProd.q3 = pqA->q0 * pqB->q3 + pqA->q1 * pqB->q2 - pqA->q2 * pqB->q1 + pqA->q3 * pqB->q0;
978 
979  // copy the result back into qA
980  *pqA = qProd;
981 
982  return;
983 }
984 
985 // function compute the quaternion product conjg(qA) * qB
986 Quaternion qconjgAxB(const Quaternion *pqA, const Quaternion *pqB)
987 {
988  Quaternion qProd;
989 
990  qProd.q0 = pqA->q0 * pqB->q0 + pqA->q1 * pqB->q1 + pqA->q2 * pqB->q2 + pqA->q3 * pqB->q3;
991  qProd.q1 = pqA->q0 * pqB->q1 - pqA->q1 * pqB->q0 - pqA->q2 * pqB->q3 + pqA->q3 * pqB->q2;
992  qProd.q2 = pqA->q0 * pqB->q2 + pqA->q1 * pqB->q3 - pqA->q2 * pqB->q0 - pqA->q3 * pqB->q1;
993  qProd.q3 = pqA->q0 * pqB->q3 - pqA->q1 * pqB->q2 + pqA->q2 * pqB->q1 - pqA->q3 * pqB->q0;
994 
995  return qProd;
996 }
997 
998 // function normalizes a rotation quaternion and ensures q0 is non-negative
1000 {
1001  float fNorm; // quaternion Norm
1002 
1003  // calculate the quaternion Norm
1004  fNorm = sqrtf(pqA->q0 * pqA->q0 + pqA->q1 * pqA->q1 + pqA->q2 * pqA->q2 + pqA->q3 * pqA->q3);
1005  if (fNorm > CORRUPTQUAT)
1006  {
1007  // general case
1008  fNorm = 1.0F / fNorm;
1009  pqA->q0 *= fNorm;
1010  pqA->q1 *= fNorm;
1011  pqA->q2 *= fNorm;
1012  pqA->q3 *= fNorm;
1013  }
1014  else
1015  {
1016  // return with identity quaternion since the quaternion is corrupted
1017  pqA->q0 = 1.0F;
1018  pqA->q1 = pqA->q2 = pqA->q3 = 0.0F;
1019  }
1020 
1021  // correct a negative scalar component if the function was called with negative q0
1022  if (pqA->q0 < 0.0F)
1023  {
1024  pqA->q0 = -pqA->q0;
1025  pqA->q1 = -pqA->q1;
1026  pqA->q2 = -pqA->q2;
1027  pqA->q3 = -pqA->q3;
1028  }
1029 
1030  return;
1031 }
1032 
1033 // set a quaternion to the unit quaternion
1034 void fqAeq1(Quaternion *pqA)
1035 {
1036  pqA->q0 = 1.0F;
1037  pqA->q1 = pqA->q2 = pqA->q3 = 0.0F;
1038 
1039  return;
1040 }
1041 
1042 // function computes the rotation quaternion that rotates unit vector u onto unit vector v as v=q*.u.q
1043 // using q = 1/sqrt(2) * {sqrt(1 + u.v) - u x v / sqrt(1 + u.v)}
1044 void fveqconjgquq(Quaternion *pfq, float fu[], float fv[])
1045 {
1046  float fuxv[3]; // vector product u x v
1047  float fsqrt1plusudotv; // sqrt(1 + u.v)
1048  float ftmp; // scratch
1049 
1050  // compute sqrt(1 + u.v) and scalar quaternion component q0 (valid for all angles including 180 deg)
1051  fsqrt1plusudotv = sqrtf(fabsf(1.0F + fu[CHX] * fv[CHX] + fu[CHY] * fv[CHY] + fu[CHZ] * fv[CHZ]));
1052  pfq->q0 = ONEOVERSQRT2 * fsqrt1plusudotv;
1053 
1054  // calculate the vector product uxv
1055  fuxv[CHX] = fu[CHY] * fv[CHZ] - fu[CHZ] * fv[CHY];
1056  fuxv[CHY] = fu[CHZ] * fv[CHX] - fu[CHX] * fv[CHZ];
1057  fuxv[CHZ] = fu[CHX] * fv[CHY] - fu[CHY] * fv[CHX];
1058 
1059  // compute the vector component of the quaternion
1060  if (fsqrt1plusudotv != 0.0F)
1061  {
1062  // general case where u and v are not anti-parallel where u.v=-1
1063  ftmp = ONEOVERSQRT2 / fsqrt1plusudotv;
1064  pfq->q1 = -fuxv[CHX] * ftmp;
1065  pfq->q2 = -fuxv[CHY] * ftmp;
1066  pfq->q3 = -fuxv[CHZ] * ftmp;
1067  }
1068  else
1069  {
1070  // degenerate case where u and v are anti-aligned and the 180 deg rotation quaternion is not uniquely defined.
1071  // first calculate the un-normalized vector component (the scalar component q0 is already set to zero)
1072  pfq->q1 = fu[CHY] - fu[CHZ];
1073  pfq->q2 = fu[CHZ] - fu[CHX];
1074  pfq->q3 = fu[CHX] - fu[CHY];
1075  // and normalize the quaternion for this case checking for fu[CHX]=fu[CHY]=fuCHZ] where q1=q2=q3=0.
1076  ftmp = sqrtf(fabsf(pfq->q1 * pfq->q1 + pfq->q2 * pfq->q2 + pfq->q3 * pfq->q3));
1077  if (ftmp != 0.0F)
1078  {
1079  // normal case where all three components of fu (and fv=-fu) are not equal
1080  ftmp = 1.0F / ftmp;
1081  pfq->q1 *= ftmp;
1082  pfq->q2 *= ftmp;
1083  pfq->q3 *= ftmp;
1084  }
1085  else
1086  {
1087  // final case where the three entries are equal but since the vectors are known to be normalized
1088  // the vector u must be 1/root(3)*{1, 1, 1} or -1/root(3)*{1, 1, 1} so simply set the
1089  // rotation vector to 1/root(2)*{1, -1, 0} to cover both cases
1090  pfq->q1 = ONEOVERSQRT2;
1091  pfq->q2 = -ONEOVERSQRT2;
1092  pfq->q3 = 0.0F;
1093  }
1094  }
1095 
1096  return;
1097 }
void f3DOFMagnetometerMatrixNED(float fR[][3], float fBc[])
Aerospace NED magnetometer 3DOF flat eCompass function, computing rotation matrix fR...
Definition: orientation.c:215
void fRotationMatrixFromQuaternion(float R[][3], const Quaternion *pq)
compute the rotation matrix from an orientation quaternion
Definition: orientation.c:822
float fatan_deg(float x)
#define CHY
Used to access Y-channel entries in various data data structures.
Definition: sensor_fusion.h:77
float fasin_deg(float x)
void f3DOFTiltNED(float fR[][3], float fGc[])
Aerospace NED accelerometer 3DOF tilt function, computing rotation matrix fR.
#define ONEOVERSQRT2
1/sqrt(2)
void fAndroidAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg, float *pfRhoDeg, float *pfChiDeg)
extract the Android angles in degrees from the Android rotation matrix
Definition: orientation.c:564
float q0
scalar component
Definition: orientation.h:39
Lower level sensor fusion interface.
#define FPIOVER180
degrees to radians conversion = pi / 180
Definition: sensor_fusion.h:99
Functions to convert between various orientation representations.
void fNEDAnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg, float *pfRhoDeg, float *pfChiDeg)
extract the NED angles in degrees from the NED rotation matrix
Definition: orientation.c:508
void feCompassAndroid(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
Android: basic 6DOF e-Compass function, computing rotation matrix fR and magnetic inclination angle f...
Definition: orientation.c:359
float q3
z vector component
Definition: orientation.h:42
void fQuaternionFromRotationMatrix(float R[][3], Quaternion *pq)
compute the orientation quaternion from a 3x3 rotation matrix
Definition: orientation.c:781
void fqAeq1(Quaternion *pqA)
set a quaternion to the unit quaternion
Definition: orientation.c:1034
Quaternion qconjgAxB(const Quaternion *pqA, const Quaternion *pqB)
function compute the quaternion product conjg(qA) * qB
Definition: orientation.c:986
void fLPFOrientationQuaternion(Quaternion *pq, Quaternion *pLPq, float flpf, float fdeltat, float fOmega[])
function low pass filters an orientation quaternion and computes virtual gyro rotation rate ...
Definition: orientation.c:912
void f3DOFMagnetometerMatrixWin8(float fR[][3], float fBc[])
Windows 8 magnetometer 3DOF flat eCompass function, computing rotation matrix fR. ...
Definition: orientation.c:275
void fQuaternionFromRotationVectorDeg(Quaternion *pq, const float rvecdeg[], float fscaling)
computes normalized rotation quaternion from a rotation vector (deg)
Definition: orientation.c:713
void feCompassWin8(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
Win8: basic 6DOF e-Compass function, computing rotation matrix fR and magnetic inclination angle fDel...
Definition: orientation.c:433
quaternion structure definition
Definition: orientation.h:37
void qAeqBxC(Quaternion *pqA, const Quaternion *pqB, const Quaternion *pqC)
function compute the quaternion product qB * qC
Definition: orientation.c:958
#define R
Definition: status.c:48
void fveqconjgquq(Quaternion *pfq, float fu[], float fv[])
function computes the rotation quaternion that rotates unit vector u onto unit vector v as v=q*...
Definition: orientation.c:1044
Math approximations file.
The sensor_fusion.h file implements the top level programming interface.
#define CHZ
#define F180OVERPI
radians to degrees conversion = 180 / pi
Matrix manipulation functions.
void f3DOFTiltAndroid(float fR[][3], float fGc[])
Android accelerometer 3DOF tilt function computing, rotation matrix fR.
#define CORRUPTQUAT
Definition: orientation.c:52
#define CHX
Used to access X-channel entries in various data data structures.
Definition: sensor_fusion.h:76
void f3x3matrixAeqI(float A[][3])
function sets the 3x3 matrix A to the identity matrix
Definition: matrix.c:45
void feCompassNED(float fR[][3], float *pfDelta, float *pfsinDelta, float *pfcosDelta, float fBc[], float fGc[], float *pfmodBc, float *pfmodGc)
NED: basic 6DOF e-Compass function, computing rotation matrix fR and magnetic inclination angle fDelt...
Definition: orientation.c:286
void fqAeqNormqA(Quaternion *pqA)
function normalizes a rotation quaternion and ensures q0 is non-negative
Definition: orientation.c:999
#define ONEOVER3840
1 / 3840
#define ONEOVER48
1 / 48
void fRotationVectorDegFromQuaternion(Quaternion *pq, float rvecdeg[])
computes rotation vector (deg) from rotation quaternion
Definition: orientation.c:862
void f3x3matrixAeqScalar(float A[][3], float Scalar)
function sets every entry in the 3x3 matrix A to a constant scalar
Definition: matrix.c:109
#define SMALLQ0
Definition: orientation.c:51
int16_t int16
Definition: sensor_fusion.h:56
void f3DOFTiltWin8(float fR[][3], float fGc[])
Windows 8 accelerometer 3DOF tilt function computing, rotation matrix fR.
float q2
y vector component
Definition: orientation.h:41
void qAeqAxB(Quaternion *pqA, const Quaternion *pqB)
function compute the quaternion product qA = qA * qB
Definition: orientation.c:969
float facos_deg(float x)
int8_t int8
Definition: sensor_fusion.h:55
float q1
x vector component
Definition: orientation.h:40
float fatan2_deg(float y, float x)
void f3DOFMagnetometerMatrixAndroid(float fR[][3], float fBc[])
Android magnetometer 3DOF flat eCompass function, computing rotation matrix fR.
Definition: orientation.c:245
void fWin8AnglesDegFromRotationMatrix(float R[][3], float *pfPhiDeg, float *pfTheDeg, float *pfPsiDeg, float *pfRhoDeg, float *pfChiDeg)
extract the Windows 8 angles in degrees from the Windows 8 rotation matrix
Definition: orientation.c:621